﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.WindowsAzure.StorageClient;
using System.Runtime.Remoting.Messaging;

namespace AntsCode.StorageClientExtensions
{
    public class XTableServiceQuery<T> : IQueryable<T>, IXTableServiceQuery
    {
        delegate IEnumerable<T> ExDelegate();

        internal XTableServiceQuery(TableServiceContext context, EventHandler<XTableServiceSendingRequestEventArgs> sendingRequest, RetryPolicy retryPolicy, string entitySetName)
        {
            this.Context = context;
            this.Provider = new XTableServiceQueryProvider(context, sendingRequest, retryPolicy);
            this.Expression = System.Linq.Expressions.Expression.Constant(this);
            this.EntitySetName = entitySetName;
        }

        internal XTableServiceQuery(XTableServiceQueryProvider provider, Expression expression)
        {
            if (provider == null)
            {
                throw new ArgumentNullException("provider");
            }

            if (expression == null)
            {
                throw new ArgumentNullException("expression");
            }

            if (!typeof(IQueryable<T>).IsAssignableFrom(expression.Type))
            {
                throw new ArgumentOutOfRangeException("expression");
            }

            this.Provider = provider;
            this.Expression = expression;
        }

        public IEnumerator<T> GetEnumerator()
        {
            return Execute().GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return Execute().GetEnumerator();
        }

        public IEnumerable<T> Execute()
        {
            var provider = (XTableServiceQueryProvider)this.Provider;
            provider.ContinuationToken = this.ContinuationToken;

            var data = this.Provider.Execute<IEnumerable<T>>(this.Expression);
            this.ContinuationToken = provider.ContinuationToken;

            return data;
        }

        public IAsyncResult BeginExecute(AsyncCallback callback, object state)
        {
            ExDelegate exDel = new ExDelegate(this.Execute);
            return exDel.BeginInvoke(callback, this);
        }

        public IEnumerable<T> EndExecute(IAsyncResult asyncResult)
        {
            AsyncResult result = (AsyncResult)asyncResult;
            ExDelegate exDel = (ExDelegate)result.AsyncDelegate;
            return exDel.EndInvoke(asyncResult);
        }

        public Type ElementType
        {
            get { return typeof(T); }
        }

        public System.Linq.Expressions.Expression Expression
        {
            get;
            set;
        }

        public TableServiceContext Context
        {
            get;
            private set;
        }

        public IQueryProvider Provider
        {
            get;
            private set;
        }

        public string EntitySetName
        {
            get;
            set;
        }

        public ContinuationToken ContinuationToken
        {
            get;
            set;
        }

        internal class XTableServiceOrderedQuery : XTableServiceQuery<T>, IOrderedQueryable<T>, IQueryable<T>, IEnumerable<T>, IOrderedQueryable, IQueryable, IEnumerable
        {
            internal XTableServiceOrderedQuery(Expression expression, XTableServiceQueryProvider provider)
                : base(provider, expression)
            { 
            
            }
        }
    }
}
